import json
import requests
import urllib.parse as urlparse
import jwt
import time
import hashlib
import sys

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BID = "bid"
  ASK = "ask"

class State(Enum):
  WAIT = "wait"
  WATCH = "watch"
  DONE = "done"
  CANCEL = "cancel"

class OrderType(Enum):
  LIMIT = "limit"
  PRICE = "price"
  MARKET = "market"

class UpbitAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, query: str):
    payload = {
      'access_key': self._key.access_key,
      # nonce in microsecond
      'nonce': str(int(time.time() * 1000 * 1000)),
      # 'nonce': str(uuid.uuid4()),
    }

    if query:
      m = hashlib.sha512()
      m.update(query.encode())
      query_hash = m.hexdigest()

      payload.update({'query_hash': query_hash})
      payload.update({'query_hash_alg': 'SHA512'})

    return jwt.encode(payload, self._key.secret_key).decode()

  def __call__(self, req: requests.PreparedRequest):
    parsed = urlparse.urlparse(req.url)

    signature = self._gen_signature(parsed.query)

    req.headers.update({'Authorization': "Bearer {}".format(signature)})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class UpbitPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = UpbitAuth(key_path)
    self._url = "https://api.upbit.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_order(
    self,
    market: str = None,
    uuids: list = None,
    identifiers: list = None,
    state: State = None,
  ):
    method = "GET"
    params = {
      'market': market,
      'uuids': uuids,
      'identifiers': identifiers,
      'state': state and state.value,
    }
    path = "/v1/orders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(self, uuid: str = None, identifier: str = None):
    one_and_only_one = (uuid == None and identifier != None) or (uuid != None and identifier == None)
    assert one_and_only_one, "uuid and identifier have one and only one!"

    method = "DELETE"
    params = {
      'uuid': uuid,
      'identifier': identifier,
    }
    path = "/v1/order"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def submit_order(
    self,
    market: str,
    side: Side,
    volume: str,
    price: str,
    ord_type: OrderType,
    identifier: str = None
  ):
    assert market, "market must be not None and not empty!"
    assert side, "side must be not None!"
    assert volume, "volume must be not None and not empty!"
    assert price, "price must be not None and not empty!"
    assert ord_type, "ord_type must be not None!"

    method = "POST"
    json = {
      'market': market,
      'side': side.value,
      'volume': volume,
      'price': price,
      'ord_type': ord_type.value,
      'identifier': identifier,
    }
    path = "/v1/orders"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

# api doc url
# global https://global-docs.upbit.com/docs
# kr     https://docs.upbit.com/docs

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  upbit = UpbitPrivateClient(key_path)
  info = upbit.get_order()
  # info = upbit.cancel_order('12345')
  # info = upbit.submit_order("KRW-BTC", Side.ASK, "0.000001", "10000000", OrderType.LIMIT)
  print(json.dumps(info, indent=2))
